package com.yidian.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.yidian.dao.BrandMapper;
import com.yidian.dao.SpecMapper;
import com.yidian.factory.RestClientFactory;
import com.yidian.server.goods.SkuSearchService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author whb
 * @create 2021-06-29 11:11
 * @desciption 从elasticsearch查询Sku数据的Servcie接口的实现类
 */
@Service
public class SkuSearchServiceImpl implements SkuSearchService {
    @Value("${hostname}")
    private String hostname;
    @Value("${port}")
    private Integer port;
    @Autowired
    private BrandMapper brandMapper;
    @Autowired
    private SpecMapper specMapper;

    @Override
    public Map search(Map<String, String> searchMap) {
        RestHighLevelClient highLevelClient = RestClientFactory.getRestHighLevelClient(hostname,port);

        //封装查询条件
        SearchRequest searchRequest=new SearchRequest("sku");
        searchRequest.types("doc");//设置查询类型

        SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();//新建查询根构建器
        BoolQueryBuilder boolQueryBuilder= QueryBuilders.boolQuery();//构建布尔查询构建器

        //关键字搜索
        MatchQueryBuilder matchQueryBuilder=QueryBuilders.matchQuery("name",searchMap.get("keywords"));
        boolQueryBuilder.filter(matchQueryBuilder);

        //商品分类过滤
        if (searchMap.get("categoryName")!=null){
            TermQueryBuilder termQueryBuilder=QueryBuilders.termQuery("categoryName",searchMap.get("categoryName"));
            boolQueryBuilder.filter(termQueryBuilder);
        }

        //商品品牌过滤
        if (searchMap.get("brand")!=null){
            TermsQueryBuilder termsQueryBuilder=QueryBuilders.termsQuery("brandName",searchMap.get("brand"));
            boolQueryBuilder.filter(termsQueryBuilder);
        }

        //商品规格过滤
        for (String key:searchMap.keySet()){
            if (key.startsWith("spec.")){
                StringBuffer sb=new StringBuffer();
                String[] valueArr=searchMap.get(key).split(" ");
                for (int i=0;i<valueArr.length;i++){
                    if (i==valueArr.length-1){
                       sb.append(valueArr[i]);
                    }else {
                        sb.append(valueArr[i]+"+");
                    }

                }
                //TermQueryBuilder termQueryBuilder=QueryBuilders.termQuery(key+".keyword",searchMap.get(key));
                TermQueryBuilder termQueryBuilder=QueryBuilders.termQuery(key+".keyword",sb.toString());
                boolQueryBuilder.filter(termQueryBuilder);
            }
        }

        //价格过滤
        if (searchMap.get("price") != null) {
            String[] price = searchMap.get("price").split("-");
            if (!"0".equals(price[0])){
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(price[0]+"00");
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
            if (!"*".equals(price[1])){
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").lte(price[1]+"00");
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
        }

        searchSourceBuilder.query(boolQueryBuilder);

        //聚合查询（商品分类）
        TermsAggregationBuilder aggregationBuilder= AggregationBuilders.terms("sku_category").field("categoryName");
        searchSourceBuilder.aggregation(aggregationBuilder);

        //分页设置
        Integer pageNo=Integer.parseInt(searchMap.get("pageNo"));//当前页码
        Integer pageSize=30;//每页显示条数（页大小）
        Integer fromIndex=(pageNo-1)*pageSize;//起始记录下标
        searchSourceBuilder.from(fromIndex).size(pageSize);//elasticsearch高级客户端设置查询页码起始索引和页大小

        //高亮设置
        HighlightBuilder highlightBuilder=new HighlightBuilder();
        highlightBuilder.field("name").preTags("<font style='color:red;'>").postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);

        //排序
        String sort = searchMap.get("sort");//排序字段
        String sortOrder = searchMap.get("sortOrder");//排序规则
        if (!"".equals(sort)){
            searchSourceBuilder.sort(sort, SortOrder.valueOf(sortOrder));
        }

        searchRequest.source(searchSourceBuilder);

        //封装查询结果
        Map resultMap=new HashMap();
        try {
            SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchHits = searchResponse.getHits();
            long totalHits = searchHits.getTotalHits();
            System.out.println("记录数："+totalHits);
            SearchHit[] hits = searchHits.getHits();

            //封装商品列表
            List<Map<String,Object>> skuList=new ArrayList<>();
            for (SearchHit hit:hits){
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();

                //用高亮name替换原来的name
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField highlightName = highlightFields.get("name");
                Text[] highlightNameArr = highlightName.fragments();
                sourceAsMap.put("name",highlightNameArr[0].toString());
                //sourceAsMap.put("name","<font style='color:red;'>华为</font> HUAWEI nova 3i 全面屏高清四摄游戏手机6GB+64GB 亮黑色 全网通移动联通电信4G手机双卡双待");

                skuList.add(sourceAsMap);
            }
            resultMap.put("rows",skuList);

            //商品分类列表
            Aggregations aggregations = searchResponse.getAggregations();
            Map<String, Aggregation> aggregationMap = aggregations.getAsMap();
            Terms terms = (Terms) aggregationMap.get("sku_category");
            List<? extends Terms.Bucket> buckets = terms.getBuckets();
            List<String> categoryList=new ArrayList<>();
            for (Terms.Bucket bucket:buckets){
                categoryList.add(bucket.getKeyAsString());
            }
            resultMap.put("categoryList",categoryList);

            //判断是否选择了商品分类，如果没有，则使用分类列表中的第一个分类名称
            String categoryName="";
            if (searchMap.get("categoryName")==null){
                if (categoryList.size()>0){
                    categoryName=categoryList.get(0);
                }
            }else {
                categoryName=searchMap.get("categoryName");
            }
            //品牌列表
            if (searchMap.get("brand")==null){//查询条件中有品牌信息，则不查询品牌信息。因为有品牌信息说明已经选择了品牌，则品牌信息面板不显示
                List<Map> brandList = brandMapper.findBrandListByCategoryName(categoryName);//根据商品分类名称查询品牌列表
                resultMap.put("brandList",brandList);
            }

            //规格列表
            List<Map> specList = specMapper.findSpecListByCategoryName(categoryName);
            for (Map spec:specList){
                String[] options = ((String) spec.get("options")).split(",");
                spec.put("options",options);
            }
            resultMap.put("specList",specList);

            //计算总页数
            long totalCount = searchHits.getTotalHits();//获取总记录数
            long totalPages = totalCount%pageSize==0 ? totalCount/pageSize : totalCount/pageSize+1;//计算出总页数
            resultMap.put("totalPages",totalPages);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return resultMap;
    }
}
